package com.ljy.system.marvel_sys.common.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.ljy.system.marvel_sys.common.shiro.cache.ShiroCacheManager;
import com.ljy.system.marvel_sys.common.shiro.kickout.KickoutSessionControlFilter;
import com.ljy.system.marvel_sys.common.shiro.realm.MyShiroRealm;
import com.ljy.system.marvel_sys.common.shiro.session.ShiroSessionDAO;
import com.ljy.system.marvel_sys.common.shiro.web.LogoutFilter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author: LiJunYi
 * @ClassName: ShiroConfig
 * @Description TODO:shiro配置
 * @Date: Created in 13:49 2019/07/03
 * @Version 1.0
 */
@Configuration
public class ShiroConfig {

    @Autowired
    @Qualifier("shiroCacheManager")
    private ShiroCacheManager shiroCacheManager;

    /**
     * 退出过滤器
     */
    public LogoutFilter logoutFilter(){
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setCacheManeger(shiroCacheManager);
        logoutFilter.setLoginUrl("/sys/index");
        return logoutFilter;
    }


    /**
     *  shiroFilter相关配置
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        // 拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 设置login URL
        shiroFilterFactoryBean.setLoginUrl("/sys/index");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/page/index");
        // 未授权的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/page/nopermission");
        // 静态资源
        filterChainDefinitionMap.put("/static/css/**","anon");
        filterChainDefinitionMap.put("/static/fonts/**","anon");
        filterChainDefinitionMap.put("/static/images/**","anon");
        filterChainDefinitionMap.put("/static/js/**","anon");
        //  登录
        filterChainDefinitionMap.put("/sys/**","anon");
        filterChainDefinitionMap.put("/druid/**","anon");
        /* 退出系统*/
        filterChainDefinitionMap.put("/logout","logout");
        /*现有资源的角色*/
        filterChainDefinitionMap.put("/**","kickout,authc");
        // 限制登录
        Map<String, Filter> filters = new LinkedHashMap<>();
        filters.put("kickout",kickoutSessionControlFilter());
        shiroFilterFactoryBean.setFilters(filters);
        // 注销成功，则跳转到指定页面
        filters.put("logout", logoutFilter());
        shiroFilterFactoryBean.setFilters(filters);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }


    /**
     *  自定义Realm
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        /*自定义realm*/
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        myShiroRealm.setCacheManager(shiroCacheManager);
        return myShiroRealm;
    }

    /**
	 * 凭证匹配器 （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
	 * 所以我们需要修改下doGetAuthenticationInfo中的代码; )
	 */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列的次数，比如散列两次，相当于md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(1024);
        return hashedCredentialsMatcher;
    }


    /**
     *  配置安全管理器  securityManager
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入自定义的realm;
        securityManager.setRealm(myShiroRealm());
        // 注入缓存管理器
        securityManager.setCacheManager(shiroCacheManager);
        // 注入自定义的session管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }


    /**
     * 会话管理器
     * 改版 redis存储会话
     */
    @Bean
    public SessionManager sessionManager()
    {
        /* Web环境非HttpSession会话管理器*/
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //注入自定义的sessionDAO
        sessionManager.setSessionDAO(sessionDAO());
        return sessionManager;
    }

    /**
     * 配置自定义会话DAO
     * @return {@link SessionDAO}
     */
    @Bean
    public SessionDAO sessionDAO()
    {
        SessionDAO sessionDAO = new ShiroSessionDAO(shiroCacheManager);
        return sessionDAO;
    }


    /**
     * 同一个用户多设备登录限制
     */
    public KickoutSessionControlFilter kickoutSessionControlFilter(){
        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
        kickoutSessionControlFilter.setCacheManager(shiroCacheManager);
        kickoutSessionControlFilter.setSessionManager(sessionManager());
        // 同一个用户最大的会话数，默认-1无限制；比如2的意思是同一个用户允许最多同时两个人登录
        kickoutSessionControlFilter.setMaxSession(1);
        // 是否踢出后来登录的，默认是false；即后者登录的用户踢出前者登录的用户；踢出顺序
        kickoutSessionControlFilter.setKickoutAfter(false);
        // 设置踢出后的地址，跳到登录界面
        kickoutSessionControlFilter.setKickoutUrl("/sys/index?kickout=1");
        return kickoutSessionControlFilter;
    }

    /**
     * 在thymeleaf 使用shiro页面标签
     * */
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

    /**
     * 开启shiro aop注解支持 使用代理方式;所以需要开启代码支持;
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * DefaultAdvisorAutoProxyCreator，Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理。
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultApp = new DefaultAdvisorAutoProxyCreator();
        defaultApp.setProxyTargetClass(true);
        return defaultApp;
    }
}
